﻿
Imports System.IO
Imports std
Imports xmlctrl
Imports xhtmlctrl

Public Class antiInputSpam(Of RETIRE_SECONDS As _int64, INTERVAL_LIMIT_SECONDS As _int64)
    Inherits container

    Public Const onsubmitFunctionName As String = "anspsub"
    Private Shared ANTIINPUTSPAM_BLOCK_BY_TIME As Int64 = 0
    Private Shared ANTIINPUTSPAM_BLOCK_BY_CHECKSUM As Int64 = 0
    Private Shared iptable As cacheManager(Of String, pair(Of String, String)) = Nothing
    Private Shared lastCheck As cacheManager(Of String, Boolean) = Nothing
    Private Shared retire_ticks As Int64 = 0
    Private Shared interval_limit_ticks As Int64 = 0

    Shared Sub New()
        retire_ticks = secondsToTicks(+(alloc(Of RETIRE_SECONDS)()))
        interval_limit_ticks = secondsToTicks(+(alloc(Of INTERVAL_LIMIT_SECONDS)()))
        ANTIINPUTSPAM_BLOCK_BY_TIME = counter.registerCounter("ANTIINPUTSPAM_BLOCK_BY_TIME", 128)
        ANTIINPUTSPAM_BLOCK_BY_CHECKSUM = counter.registerCounter("ANTIINPUTSPAM_BLOCK_BY_CHECKSUM", 128)
        iptable = New cacheManager(Of String, pair(Of String, String)) _
                                  (threadsafe:=True, cachesize:=1024, RETIRETICKS:=retire_ticks)
        lastCheck = New cacheManager(Of String, Boolean) _
                                    (threadsafe:=True, cachesize:=1024, retireticks:=interval_limit_ticks)
    End Sub

    Private Shared Function createRndInt() As String
        Return Convert.ToString(rnd(0, 100, True))
    End Function

    Private Shared Function createExpression(ByRef value As String) As String
        Dim rtn As String = Nothing
        Dim bracket As Int32 = 0
        For i As Int64 = 0 To 3
            Dim bracketTmp As Int32
            bracketTmp = rnd(0, 3, True)
            If bracketTmp = 0 Then
                bracket += 1
                rtn += character.leftBracket
            End If
            rtn += createRndInt()
            If bracketTmp = 1 AndAlso bracket > 0 Then
                bracket -= 1
                rtn += character.rightBracket
            End If
            Dim tmp As Int64 = 0
            tmp = rnd(0, 4, True)
            Select Case tmp
                Case 0
                    rtn += character.plusSign
                Case 1
                    rtn += character.minusSign
                Case 2
                    rtn += character.multiplicationSign
                Case 3
                    rtn += character.divisionSign
            End Select
        Next
        rtn += createRndInt()
        While bracket > 0
            bracket -= 1
            rtn += character.rightBracket
        End While
        Dim v As Double = Nothing
        assert(matrix.expression.calculate(rtn, v))
        value = Convert.ToString(Math.Floor(v))
        Return rtn
    End Function

    Private Shared Function setIpTable(ByVal ip As String) As String
        Dim rtn As pair(Of String, String) = Nothing
        If Not iptable.get(ip, rtn) Then
            Dim exp As String = Nothing
            Dim value As String = Nothing
            exp = createExpression(value)
            rtn = make_pair(exp, value)
        End If
        iptable.add(ip, rtn)
        Return rtn.first
    End Function

    Public Shared Function checkIpTable(ByVal ip As String, ByVal checksum As String) As Boolean
        If isemptystring(checksum) Then
            counter.increase(ANTIINPUTSPAM_BLOCK_BY_CHECKSUM)
            Return False
        ElseIf lastCheck.get(ip, Nothing) Then
            counter.increase(ANTIINPUTSPAM_BLOCK_BY_TIME)
            Return False
        Else
            Dim p As pair(Of String, String) = Nothing
            If iptable.get(ip, p) Then
                lastCheck.add(ip, False)
                If strsame(p.second, checksum) Then
                    Return True
                Else
                    counter.increase(ANTIINPUTSPAM_BLOCK_BY_CHECKSUM)
                    Return False
                End If
            Else
                counter.increase(ANTIINPUTSPAM_BLOCK_BY_CHECKSUM)
                Return False
            End If
        End If
    End Function

    Protected Overrides Sub setfather(ByVal c As xmlcontrol)
        MyBase.setfather(c)
        Dim f As form = Nothing
        f = convertor.cast(Of form)(c)
        f.onsubmit += setOnsubmitFunction(Nothing, f.children().size() - 1)
    End Sub

    Public Shared Function setOnsubmitFunction(ByVal page As xhtmlpage, ByVal index As Int64) As String
        If Not page Is Nothing Then
            register(page)
        End If
        Return "return " + onsubmitFunctionName + "(this," + Convert.ToString(index) + ");"
    End Function

    Private Shared Sub register(ByVal inlineScript As inlineScript)
        assert(Not inlineScript Is Nothing)
        assert(strsame([true].name(inlineScript), "t"))
        inlineScript.addscript(onsubmitFunctionName,
                               "function " + onsubmitFunctionName + "(f,i)" +
                               "{var c=f.children[i];c.value=Math.floor(eval(c.value));return t;}")
    End Sub

    Private Shared Sub register(ByVal page As xhtmlpage)
        If Not page Is Nothing Then
            register(page.inlineScript())
        End If
    End Sub

    Public Sub New(ByVal page As xhtmlpage,
                   ByVal ip As String, ByVal query As String,
                   Optional ByVal buff As Stream = Nothing, Optional ByRef cs As String = Nothing)
        MyBase.New(buff)
        register(page)
        Dim input As input = Nothing
        input = New input(buff)
        input.type = xhtmlctrl.input.type_enum.hidden
        input.name = query
        cs = setIpTable(ip)
        input.value = cs
        addchild(input)
    End Sub
End Class
